package org.nox.helloseries.web.server;

import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.apache.commons.io.IOUtils;
import org.apache.http.HttpResponse;
import org.apache.http.client.HttpClient;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.HttpClientBuilder;
import org.nox.helloseries.configuration.CustomConfiguration;
import org.nox.helloseries.configuration.CustomConfigurationKey;
import org.nox.helloseries.domain.Qualite;
import org.nox.helloseries.domain.Serie;
import org.nox.helloseries.domain.Version;
import org.nox.helloseries.factory.HelloSeriesServiceFactory;
import org.nox.helloseries.rest.client.configuration.RESTConfigurationService;
import org.nox.helloseries.web.client.HelloSeriesService;
import org.nox.helloseries.web.client.metier.Application;
import org.nox.helloseries.web.client.metier.SerieWeb;
import org.nox.helloseries.web.client.metier.VersionWeb;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.google.gwt.user.server.rpc.RemoteServiceServlet;

/**
 * The server side implementation of the RPC service.
 */
public class HelloSeriesServiceImpl extends RemoteServiceServlet implements HelloSeriesService
{

    private static final Logger LOG = LoggerFactory.getLogger( HelloSeriesService.class );

    // Services
    private RESTConfigurationService restConfigurationService = HelloSeriesServiceFactory.createOrGetService( RESTConfigurationService.class );

    // Url de test
    private static final String REST_HOST = CustomConfiguration.get().getString( CustomConfigurationKey.URL_HELLOSERIES_REST ) + "configuration/trackers";

    private static final int DEFAULT_TIMEOUT_MS = 2000;

    private static final String[] CMD_CHECK_HELLOSERIES = { "/bin/sh", "-c", "ps -ef | grep helloseries-server.jar" };

    // La chaine que la commande CMD_CHECK_HELLOSERIES doit renvoyer
    private static final String STRING_CHECK_HELLOSERIES = "java -jar helloseries-server.jar";

    @Override
    public void setData( List<SerieWeb> listSeriesWeb )
    {
        // Transformation des séries
        List<Serie> listSeries = new ArrayList<Serie>();

        for ( SerieWeb serie : listSeriesWeb )
        {
            listSeries.add( untransformSerie( serie ) );
        }
        LOG.debug( "[HelloSeriesServiceImpl] Setting XML data" );
        restConfigurationService.saveSeries( listSeries );
        LOG.debug( "[HelloSeriesServiceImpl] Setting XML data done" );
    }

    @Override
    public List<SerieWeb> getData()
    {

        List<SerieWeb> listSeriesWeb = new ArrayList<SerieWeb>();

        LOG.debug( "[HelloSeriesServiceImpl] Getting XML Data" );
        List<Serie> listSeries = restConfigurationService.getSeries();
        LOG.debug( "[HelloSeriesServiceImpl] Getting {} series", listSeries.size() );

        if ( listSeries != null )
        {
            for ( Serie serie : listSeries )
            {
                listSeriesWeb.add( transformSerie( serie ) );
            }
        }
        return listSeriesWeb;
    }

    private SerieWeb transformSerie( Serie s1 )
    {
        SerieWeb s = new SerieWeb();
        s.setActive( s1.isActive() );
        s.setNom( s1.getNom() );
        s.setEpisode( s1.getEpisode() );
        s.setSaison( s1.getSaison() );
        s.setHd( s1.getQualite().isHd() );
        s.setVersion( VersionWeb.fromValue( s1.getVersion().getValue() ) );
        return s;
    }

    private Serie untransformSerie( SerieWeb s1 )
    {
        Serie s = new Serie();

        s.setActive( s1.isActive() );
        s.setNom( s1.getNom() );
        s.setEpisode( s1.getEpisode() );
        s.setSaison( s1.getSaison() );
        s.setQualite( s1.isHd() ? Qualite._720P : Qualite.SD );
        s.setVersion( Version.fromValue( s1.getVersion().getValue() ) );

        return s;
    }

    @Override
    public Map<Application, Boolean> getStatutsHelloSeries()
    {
        Map<Application, Boolean> status = new HashMap<Application, Boolean>();
        status.put( Application.HS_REST, getRESTServerStatus() );
        status.put( Application.HS_SERVER, getHSBatchStatus() );
        return status;
    }

    private Boolean getHSBatchStatus()
    {
        LOG.debug( "Vérification que helloseries-server.jar est lancé sur le serveur" );
        try
        {
            LOG.info( "Execution de la commande : " + CMD_CHECK_HELLOSERIES );
            Runtime r = Runtime.getRuntime();
            Process p = r.exec( CMD_CHECK_HELLOSERIES );
            p.waitFor();
            // Traitement des 2 input
            List<String> retour = IOUtils.readLines( p.getInputStream() );
            retour.addAll( IOUtils.readLines( p.getErrorStream() ) );
            LOG.debug( "retour de la commande : {}", retour );
            // Test de présence
            for ( String line : retour )
            {
                if ( line.contains( STRING_CHECK_HELLOSERIES ) )
                {
                    return true;
                }
            }
        }
        catch ( Exception e )
        {
            LOG.error( "", e );
        }
        return false;
    }

    private Boolean getRESTServerStatus()
    {
        LOG.info( "Test de disponibilité du serveur REST, url testée : {}", REST_HOST );
        try
        {

            HttpGet request = new HttpGet( REST_HOST );
            HttpResponse response = getHttpClient().execute( request );
            int responseCode = response.getStatusLine().getStatusCode();
            LOG.debug( "Code de réponse sur {} : {}", REST_HOST, responseCode );
            return ( 200 <= responseCode && responseCode <= 399 );
        }
        catch ( IOException e )
        {
            LOG.error( "Impossible de vérifier la dispo du serveur", e );
            return false;
        }
    }

    /**
     * @return un client http par defaut
     */
    private HttpClient getHttpClient()
    {
        // Configuration des timeout
        RequestConfig requestConfig = RequestConfig.custom().setConnectionRequestTimeout( DEFAULT_TIMEOUT_MS ).setConnectTimeout( DEFAULT_TIMEOUT_MS ).setSocketTimeout( DEFAULT_TIMEOUT_MS ).build();

        return HttpClientBuilder.create().setDefaultRequestConfig( requestConfig ).build();
    }
}
